package com.httpclient.httputils;

import org.apache.http.Header;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeader;
import org.apache.http.pool.PoolStats;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.ssl.TrustStrategy;

import javax.net.ssl.SSLContext;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;

/**
 * 创建httpclient连接池
 * @author A夜兔A
 * @data 2022年03月09日 9:56
 */
public class CreateHttpPool {

    private static final HttpClientBuilder httpClientBuilder= HttpClients.custom();
    static {

        /**
         * 绕过不安全的https请求证书验证
         */
        Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
                .register("http", PlainConnectionSocketFactory.INSTANCE)
                .register("https", trustCreateHttps())
                .build();

        /**
         * 一、创建连接池
         */
        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(registry);

        cm.setMaxTotal(50);//连接池最大连接,默认<=20
        /**
         * 什么是路由，一个域名就是一个路由，或者说一个ip+port就是一个路由，
         * 在同一个域名下的所有请求都是一个路由。如果域名变了，则就是新的路由
         * 如果系统只请求一个域名（只请求一个项目后端）可以将最大连接数和路由连接数设置成一样。
         */
        cm.setDefaultMaxPerRoute(50);//每个路由默认有多少个连接，默认<=2

        System.out.println("连接池的最大连接数"+cm.getMaxTotal());
        System.out.println("每个路由的最大连接数"+cm.getDefaultMaxPerRoute());
        PoolStats totalStats = cm.getTotalStats();//连接池状态
        System.out.println("连接池的最大连接数"+totalStats.getMax());
        System.out.println("连接池有多少连接被占用"+totalStats.getLeased());
        System.out.println("连接池有多少连接是可用的"+totalStats.getAvailable());

        httpClientBuilder.setConnectionManager(cm);

        /**
         * 二、设置请求的默认配置
         */
        RequestConfig requestConfig=RequestConfig.custom()
                .setConnectTimeout(5000)
                .setSocketTimeout(3000)
                .setConnectionRequestTimeout(5000)
                .build();
        httpClientBuilder.setDefaultRequestConfig(requestConfig);

        /**
         * 设置默认的请求头heard
         */

        List<Header> defaultHeaders=new ArrayList<>();
        BasicHeader userAgentHeader=new BasicHeader("User_Agent","Mozilla/5.0");
        defaultHeaders.add(userAgentHeader);
        httpClientBuilder.setDefaultHeaders(defaultHeaders);
    }



    /**
     * 创建支持安全协议的连接工厂
     * @return
     */
    private static ConnectionSocketFactory trustCreateHttps() {

        SSLContextBuilder sslContextBuilder=new SSLContextBuilder();
        try {
            sslContextBuilder.loadTrustMaterial(null, new TrustStrategy() {
                @Override
                public boolean isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
                    return true;
                }
            });
            SSLContext sslContext = sslContextBuilder.build();

            SSLConnectionSocketFactory sslConnectionSocketFactory =
                    new SSLConnectionSocketFactory(sslContext, new String[]{
                            "SSLv2Hello", "SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2"
                    }, null, NoopHostnameVerifier.INSTANCE);

            return sslConnectionSocketFactory;
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        } catch (KeyStoreException e) {
            e.printStackTrace();
        }
        return null;
    }

    //使用的时候直接在方法中build就可以了


}
